package com.vt.admin.service;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.google.common.base.Joiner;
import com.google.common.collect.ListMultimap;
import com.vt.admin.api.dto.UserPermissionMiddleTab;
import com.vt.admin.api.dto.user.*;
import com.vt.admin.api.dto.user.structure.UserOrganStructure;
import com.vt.admin.api.dto.user.structure.UserOrganStructureItem;
import com.vt.admin.api.entity.SysMenu;
import com.vt.admin.api.entity.SysUser;
import com.vt.admin.api.feign.RemoteTokenService;
import com.vt.admin.exception.OrganizationTypeVerifyFailException;
import com.vt.admin.exception.PhoneAlreadyExistsException;
import com.vt.admin.exception.UsernameAlreadyExistsException;
import com.vt.admin.mapper.SysUserMapper;
import com.vt.common.core.constant.CommonConstant;
import com.vt.common.core.constant.enums.EnumLoginType;
import com.vt.common.core.constant.enums.SysUserEnum;
import com.vt.common.core.util.R;
import com.vt.common.core.util.SnowflakeIdWorker;
import com.vt.common.security.service.CustomUser;
import com.vt.common.security.util.OrganizationUtil;
import com.vt.common.security.util.SecurityUtils;
import com.vt.common.security.util.SysClient;
import lombok.AllArgsConstructor;
import org.apache.commons.lang.StringUtils;
import org.springframework.boot.CommandLineRunner;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;


@Service
@AllArgsConstructor
public class SysUserService implements CommandLineRunner {

    private final SysMenuService sysMenuService;
    private final SysUserMapper mapper;
    private final StringRedisTemplate redisTemplate;
    private final OrganizationUtil organizationUtil;
    private final SysClient sysClient;

    private final static BCryptPasswordEncoder encoder;
    private final CacheManager cacheManager;

    private final RemoteTokenService remoteTokenService;

    static {
        encoder = new BCryptPasswordEncoder();
    }

    @Cacheable(value = "user_details", key = "#username", unless = "#result == null")
    public UserInfo findUserInfo(String type, String username) {
        SysUser condition = new SysUser();
        if (EnumLoginType.PWD.getType().equals(type)) {
            condition.setAccount(username);
        } else if (EnumLoginType.MOBILE.getType().equals(type)) {
            condition.setPhone(username);
        } else if (EnumLoginType.WECHAT.getType().equals(type)) {
            condition.setWxOpenId(username);
        }

        SysUser sysUser = mapper.selectOne(new QueryWrapper(condition));

        if (null == sysUser)
            return null;

        //设置权限列表
        List<SysMenu> menus;
        if (SysUserEnum.USER_TYPE_SUPER.code == sysUser.getUserType())
            menus = sysMenuService.getAllMenuUrl();
        else
            menus = sysMenuService.getMenuUrlByUserId(sysUser.getId());

        Set<String> permissions = new HashSet<>();
        for (SysMenu menuVo : menus) {
            if (StringUtils.isNotEmpty(menuVo.getUrl())) {
                String permission = menuVo.getUrl();
                permissions.add(permission);
            }
        }

        UserInfo user = new UserInfo();
        user.setSysUser(sysUser);
        user.setPermissions(ArrayUtil.toArray(permissions, String.class));
        return user;
    }

    public VueUserInfo createVInfo() {
        return createVInfo(SecurityUtils.getUser());
    }

    private VueUserInfo createVInfo(CustomUser user) {
        return new VueUserInfo()
                .setUserId(user.getId())
                .setAccount(user.getUsername())
                .setName(user.getName())
                .setUserStatus(user.isEnabled() ? 1 : 0);
    }

    public UserAuthorities currentUserAuthority() {
        CustomUser user = SecurityUtils.getUser();

        //从数据库查询用户权限
        List<SysMenu> menus;
        if (SysUserEnum.USER_TYPE_SUPER.code == user.getUserType())
            menus = sysMenuService.getAllMenuUrl();
        else
            menus = sysMenuService.getMenuUrlByUserId(user.getId());

        //menu 权限 和 button 分开
        List<String> ms = new ArrayList();
        List<String> bs = new ArrayList();
        for (SysMenu m : menus) {
            if (null == m.getType() || StringUtils.isEmpty(m.getUrl()))
                continue;

            if (m.getType() == 1)
                ms.add(m.getUrl());
            else
                bs.add(m.getUrl());
        }

        Joiner joiner = Joiner.on(";");
        return new UserAuthorities().setUserId(user.getId()).setMenuStr(joiner.join(ms)).setBtnStr(joiner.join(bs));
    }

    public UserOrganStructure userOrganStructureInfo(String id) {
        return new UserOrganStructure(mapper.getUserOrganStructure(id));
    }

    public IPage<SysUserFullDto> list(SysUserQueryParam param) {
        List<String> orgSets = null;
        if (StringUtils.isEmpty(param.getOrganId())) {
            //默认管理公司区隔
            orgSets = sysClient.getManagementCompanyIds();
        } else if (1 == param.getOrganCascade()) {
            orgSets = new ArrayList(organizationUtil.children(param.getOrganId()));
        }

        param.setOrgSets(orgSets);
        IPage<SysUserFullDto> pageData = mapper.list(param);
        List<SysUserFullDto> list = pageData.getRecords();

        if (null != list && list.size() > 0) {
            //组织、职位、角色信息
            ListMultimap<String, UserOrganStructureItem> multiMap =
                    UserOrganStructureItem.groupByUserId(mapper.queryUserOrganStructure(list));

            String[] textArray;
            for (SysUserFullDto user : list) {
                textArray = new UserOrganStructure(multiMap.get(user.getId())).textArray();

                user.setOrganizations(textArray[0]);
                user.setPositions(textArray[1]);
                user.setRoles(textArray[2]);
            }
        }
        return pageData;
    }

    public SysUser getById(String id) {
        SysUser sysUser = mapper.selectById(id);
        sysUser.setPassword(null);
        return sysUser;
    }

    @Override
    public void run(String... args) {
        writeRedis();
    }

    @Async
    public void writeRedis() {
        List<SysUser> list = mapper.selectList(new QueryWrapper());
        List<VueUserInfo> users = new ArrayList(list.size());
        for (SysUser user : list) {
            users.add(new VueUserInfo(user));
        }
        redisTemplate.opsForValue().set(CommonConstant.REDIS_USER_PREFIX, JSONUtil.toJsonStr(users));
    }

    @Transactional
    public R<Boolean> save(SysUserFullDto user) {
        if (countByField(user, "account") > 0) {
            throw new UsernameAlreadyExistsException();
        }

        if (countByField(user, "phone") > 0) {
            throw new PhoneAlreadyExistsException();
        }

        JSONArray organizations = JSONUtil.parseArray(user.getOrganizations());
        List<String> managerIds = managerCompany(parseTwoArray(organizations));
        if (managerIds.size() != 1) {
            throw new OrganizationTypeVerifyFailException();
        }

        String uId = SecurityUtils.getUser().getId();
        Date d = new Date();

        JSONArray positions = JSONUtil.parseArray(user.getPositions());
        JSONArray roles = JSONUtil.parseArray(user.getRoles());

        //是否有变更组织结构等相关权限信息
        boolean isEditPermission = false;

        user.setUpdater(uId);
        user.setUpdateTime(d);
        if (StrUtil.isNotEmpty(user.getId())) {
            user.setPassword(null);//不更新密码
            mapper.updateById(user);
            //删除组织，角色，职位关联信息，重新写入
            mapper.deleteCascadeInfo(user.getId());
        } else {
            //密码加密
            user.setPassword(encoder.encode(user.getPassword()));
            user.setId(SnowflakeIdWorker.getId());
            user.setCreater(uId);
            user.setCreateTime(d);
            user.setUserType(1);

            mapper.insert(user);
        }


        List<UserPermissionMiddleTab> organizationList = parseJsonArray(organizations, user.getId());
        List<UserPermissionMiddleTab> positionList = parseJsonArray(positions, user.getId());
        List<UserPermissionMiddleTab> roleList = parseJsonArray(roles, user.getId());

        mapper.insertOrgans(organizationList);
        mapper.insertRoles(roleList);
        mapper.insertPoss(positionList);

        if (isEditPermission) {
            //登出用户
        }

        cacheManager.getCache("user_details").evict(user.getAccount());
        return new R(Boolean.TRUE);
    }

    private List<UserPermissionMiddleTab> parseJsonArray(JSONArray array, String userId) {
        List list = new ArrayList();

        String c = SecurityUtils.getUser().getId();
        Date d = new Date();
        JSONArray arr;
        UserPermissionMiddleTab upm;
        for (int i = 0, len = array.size(); i < len; i++) {
            arr = array.getJSONArray(i);

            for (int j = 0, len2 = arr.size(); j < len2; j++) {
                upm = new UserPermissionMiddleTab();
                upm.setId(SnowflakeIdWorker.getId());
                upm.setNo(i);
                upm.setUserId(userId);
                upm.setCreater(c);
                upm.setD(d);
                upm.setItemId(arr.getStr(j));

                list.add(upm);
            }
        }
        return list;
    }

    private List<String> parseTwoArray(JSONArray array) {
        List<String> list = new ArrayList();

        JSONArray arr;
        for (Object obj : array) {
            arr = (JSONArray) obj;
            list.addAll(arr.toList(String.class));
        }
        return list;
    }

    private List<String> managerCompany(List<String> list) {
        Set<String> set = new HashSet();
        String mId;
        for (String id : list) {
            mId = organizationUtil.searchOrgMgCompany(id);
            if (StrUtil.isNotEmpty(mId))
                set.add(mId);
        }
        return new ArrayList(set);
    }

    private int countByField(SysUserFullDto user, String field) {
        QueryWrapper wrapper = new QueryWrapper();
        if (StrUtil.isNotEmpty(user.getId()))
            wrapper.ne("id", user.getId());

        wrapper.eq(field, ReflectUtil.getFieldValue(user, field));

        return mapper.selectCount(wrapper);
    }

    public R<Boolean> resetPwd(SysUser t) {
        SysUser user = new SysUser();
        user.setId(t.getId());
        user.setPassword(encoder.encode(t.getPassword()));

        user.updateById();

        //登出用户
        //SecurityUtils.getUser()

        return new R(Boolean.TRUE);
    }

    public R<Boolean> logout(String authHeader) {
        CustomUser c = SecurityUtils.getUser();
        cacheManager.getCache("user_details").evict(c.getUsername());
        remoteTokenService.logout(authHeader);
        return new R(Boolean.TRUE);
    }

    public R<Boolean> updateState(SysUser t) {
        SysUser u = mapper.selectById(t.getId());

        new SysUser()
                .setId(t.getId())
                .setUserStatus(t.getUserStatus())
                .updateById();

        if (null != u)
            cacheManager.getCache("user_details").evict(u.getAccount());
        return new R(Boolean.TRUE);
    }
}
